#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

#include "CConnection.h"
#include "CFrameGrinder.h"

char buf[256];

sighandler_t old_pipeHandler;
sighandler_t old_ctrlcHandler;
bool isShutdown = false;
pthread_t browser_connect_thread;
pthread_t text_connect_thread;

CBrowserConnection browser;
CConnection text;
CFrameGrinder frameGrinder;

void dbgInit()
{
    FILE* fp = fopen("/home/ubuntu/dbg.log", "w");
    if (fp != NULL)
    {
        fclose(fp);
    }
}

void dbgMsg(string msg)
{
    FILE* fp = fopen("/home/ubuntu/dbg.log", "a");
    if (fp != NULL)
    {
        int bytesWritten = fwrite(msg.c_str(), sizeof (char), msg.size(), fp);
        bytesWritten = fwrite("\n", sizeof (char), 1, fp);
        fflush(NULL);
        fclose(fp);
    }
}

void dbgMsg_displayConnectionContext(const char* func, const char* msg, const CConnection& con)
{
    sprintf(buf, "%s() - %s - %s", func, msg, con.displayText().c_str());
    dbgMsg(buf);
}

void* connect_thread(void* pVoid)
{
    CConnection* pCtx = (CConnection*) pVoid;
    pCtx->resetConnection();
    while (!isShutdown)
    {
        if (pCtx->m_state == CConnection::CONNECTION_STATE_INIT)
        {
            dbgMsg_displayConnectionContext(__FUNCTION__, "Before TryToBind()  ", *pCtx);
            pCtx->tryToBind();
        }
        if (pCtx->m_state == CConnection::CONNECTION_STATE_BIND_OK)
        {
            dbgMsg_displayConnectionContext(__FUNCTION__, "bind OK, try to accept  ", *pCtx);
            CConnection::CONNECTION_STATE eTempBrowserState = CConnection::CONNECTION_STATE_INIT;
            pCtx->tryToAccept();
        }
        usleep(2000 * 1000);
    }
    pthread_exit(NULL);
}

void sigPipeHandler(int sig_num)
{
    browser.resetConnection();
    text.resetConnection();
    fflush(NULL);
}

void sigCtrlCHandler(int sig_num)
{
    isShutdown = true;
    browser.resetConnection();
    text.resetConnection();
    fflush(NULL);
}

int main(int, char**)
{
    char head[512];
    cv::VideoCapture camera;
    const std::string videoStreamAddress = "http://root:root@192.168.1.16/axis-cgi/mjpg/video.cgi?.mjpg";
    /* it may be an address of an mjpeg stream, 
    e.g. "http://user:pass@cam_address:8081/cgi/mjpg/mjpg.cgi?.mjpg" */

    dbgInit();
    browser.init(7777, (char*) "browser");
    text.init(5555, (char*) "text");

    old_pipeHandler = signal(SIGPIPE, sigPipeHandler);
    if (old_pipeHandler == SIG_ERR)
    {
        printf("We have an error\n");
    }

    old_ctrlcHandler = signal(SIGINT, sigCtrlCHandler);
    if (old_ctrlcHandler == SIG_ERR)
    {
        printf("We have an error\n");
    }

    int iRet = pthread_create(&browser.m_thread_handle, NULL, connect_thread, &browser);
    if (iRet != 0)
    {
        printf("We have an error\n");
    }
    iRet = pthread_create(&text.m_thread_handle, NULL, connect_thread, &text);
    if (iRet != 0)
    {
        printf("We have an error\n");
    }

    //open the video stream and make sure it's opened
    if (!camera.open(videoStreamAddress))
    {
        std::cout << "Error opening video stream or file" << std::endl;
        return -1;
    }

    if (camera.isOpened())
    {
        dbgMsg_displayConnectionContext(__FUNCTION__, "Camera opened OK 1 ", browser);
        dbgMsg_displayConnectionContext(__FUNCTION__, "Camera opened OK 2 ", text);
        while (1)
        {
            camera >> frameGrinder.m_frame;
            if (!frameGrinder.m_frame.empty())
            {
                frameGrinder.lookForTarget();

                if (text.m_state == CConnection::CONNECTION_STATE_ACCEPT_OK)
                {
                    text.writeClient((char*) frameGrinder.m_targetCoordinateText.c_str(), frameGrinder.m_targetCoordinateText.size());
                    fflush(NULL);
                }
                if (browser.m_state == CConnection::CONNECTION_STATE_ACCEPT_OK)
                {
                    try
                    {

                        frameGrinder.compress();

                        sprintf(head, "\r\n--informs\r\nContent-Type: image/jpeg\r\nContent-Length: %d\r\n\r\n", frameGrinder.m_outbuf.size());
                        browser.writeClient(head, strlen(head));
                        fflush(NULL);
                        browser.writeClient((char*) frameGrinder.m_outbuf.data(), frameGrinder.m_outbuf.size());
                        fflush(NULL);
                    }
                    catch (...)
                    {
                        try
                        {
                            dbgMsg("browser - exception");
                        }
                        catch (...)
                        {
                        }
                    }
                }
            }
        }
    }
    return 0;
}
